package xiaopiya.robot;

import java.util.Collection;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.RosterPacket.ItemType;

import xiaopiya.robot.handler.IMessageHandler;
import xiaopiya.robot.handler.main.IMessageFilter;
import xiaopiya.robot.handler.main.ShutterMessageFilter;
import xiaopiya.robot.system.IRosterHandler;

/**
 * @author Ray (ayufox@gmail.com)
 * @version 0.1
 */
public class XiaoPiYaRobot extends Thread
{
	private final static Log REQUEST_LOG = LogFactory
			.getLog("xiaopiya.request");

	private final static Log LOG = LogFactory.getLog(XiaoPiYaRobot.class);

	private String name;

	private String username;

	private String password;

	private XMPPConnection xmppConnection;

	private IMessageHandler messageHandler;

	private IRosterHandler rosterHandler;

	private ThreadPoolExecutor threadPoolExecutor;

	private IMessageFilter messageFilter = new ShutterMessageFilter();

	public void setThreadPoolExecutor(ThreadPoolExecutor threadPoolExecutor)
	{
		this.threadPoolExecutor = threadPoolExecutor;
	}

	public void setMessageFilter(IMessageFilter messageFilter)
	{
		this.messageFilter = messageFilter;
	}

	public XiaoPiYaRobot(String name, String username, String password,
			XMPPConnection xmppConnection, IMessageHandler messageHandler,
			IRosterHandler rosterHandler)
	{
		this.name = name;
		this.username = username;
		this.password = password;
		this.xmppConnection = xmppConnection;
		this.messageHandler = messageHandler;
		this.rosterHandler = rosterHandler;
	}

	public void startup() throws XMPPException
	{
		if (this.name == null || this.username == null || this.password == null
				|| this.xmppConnection == null || this.messageHandler == null)
		{
			throw new IllegalStateException("all parameter can't be null");
		}
		if (LOG.isInfoEnabled())
		{
			LOG.info("start robot xmpp connection[" + this.name + "]");
		}
		this.xmppConnection.connect();
		if (LOG.isInfoEnabled())
		{
			LOG.info("login xmpp server with[username=" + this.username
					+ ",password=" + this.password + "][" + this.name + "]");
		}
		this.xmppConnection.login(this.username, this.password);

		if (LOG.isInfoEnabled())
		{
			LOG.info("create listener [" + this.name + "]");
		}

		if (this.rosterHandler != null)
		{
			final Roster roster = this.xmppConnection.getRoster();
			this.rosterHandler.startup(this.username, roster);
			roster.addRosterListener(new RosterListener()
			{
				public void entriesAdded(Collection<String> list)
				{
					for (String entryName : list)
					{
						RosterEntry entry = roster.getEntry(entryName);
						try
						{
							if (REQUEST_LOG.isInfoEnabled())
							{
								REQUEST_LOG.info("addFriend[" + entry + "]");
							}
							rosterHandler.addFriend(entry);
						} catch (Throwable t)
						{
							LOG.error("error when handle addFriend", t);
						}
						entry = null;
					}
				}

				public void entriesDeleted(Collection<String> list)
				{
					if (REQUEST_LOG.isInfoEnabled())
					{
						REQUEST_LOG.info("entriesDeleted[" + list + "]");
					}
					if (LOG.isInfoEnabled())
					{
						LOG.info("entriesDeleted:" + list);
					}
				}

				public void entriesUpdated(Collection<String> list)
				{
					if (REQUEST_LOG.isInfoEnabled())
					{
						REQUEST_LOG.info("entriesUpdated[" + list + "]");
					}
					if (LOG.isInfoEnabled())
					{
						LOG.info("entriesUpdated:" + list);
					}

					for (String name : list)
					{
						RosterEntry entry = roster.getEntry(name);

						if (ItemType.none == entry.getType())
						{
							LOG.info("deleteEntry:" + entry);
							try
							{
								rosterHandler.removeFriend(entry);
							} catch (Throwable t)
							{
								LOG.error("error when handle addFriend", t);
							}
							try
							{
								roster.removeEntry(entry);
							} catch (XMPPException e)
							{
								LOG.error("error when removeEntry", e);
							}
						}
						entry = null;
					}
				}

				public void presenceChanged(Presence pre)
				{
					rosterHandler.updatePresence(pre);
				}
			});
		}
		ChatManager chatmanager = this.xmppConnection.getChatManager();
		chatmanager.addChatListener(new ChatManagerListener()
		{
			public void chatCreated(Chat chat, boolean createdLocally)
			{
				chat.addMessageListener(new MessageListener()
				{
					public void processMessage(final Chat chat,
							final Message message)
					{

						if (REQUEST_LOG.isInfoEnabled())
						{
							REQUEST_LOG.info("handleRequest["
									+ message.getBody() + "] from ["
									+ message.getFrom() + "] for robot ["
									+ username + "]");
						}

						String from = message.getFrom().split("/")[0];

						if ("msgpush@cdream.com".equals(from)
								|| from.endsWith("www.myspace.cn"))
						{
							if (LOG.isInfoEnabled())
							{
								LOG.info("message[" + message.getBody()
										+ "] from mppush");
							}
						} else if (threadPoolExecutor == null)
						{
							handle(chat, message);
						} else
						{
							threadPoolExecutor.execute(new Runnable()
							{
								public void run()
								{
									handle(chat, message);
								}
							});
						}
					}

					private void handle(Chat chat, Message message)
					{
						try
						{
							String messageBody = messageFilter.filter(message
									.getBody());
							messageHandler.handle(xmppConnection, chat,
									message, messageBody);
						} catch (Throwable e)
						{
							LOG.error("error when handle message", e);
						}
					}
				});
			}
		});

		if (LOG.isInfoEnabled())
		{
			LOG.info("start robot success [" + this.name + "]");
		}
	}

	public void shutdown()
	{
		if (LOG.isInfoEnabled())
		{
			LOG.info("shutdown robot[" + this.name + "]");
		}
		if (this.xmppConnection != null)
		{
			this.xmppConnection.disconnect();
		}
		if (LOG.isInfoEnabled())
		{
			LOG.info("shutdown robot[" + this.name + "] sucess!");
		}
	}
}
